#include "Renderizable.h"

#include <glm\gtc\matrix_inverse.hpp>


Renderizable::Renderizable() : 
	m_coefDifuso(0.0f, 0.0f, 0.0f),
	m_coefAmbiente(0.0f, 0.0f, 0.0f),
	m_coefEspecular(0.0f, 0.0f, 0.0f),
	m_posLuzDireccional(1.0f, 1.0f, 10.0f)
{
}


Renderizable::~Renderizable() { }


void Renderizable::init(const glm::vec3 &posFuenteLuzDireccional, std::string vertShaderFileName, std::string fragShaderFileName) {
	// La posicion de la luz direccional
	this->m_posLuzDireccional = posFuenteLuzDireccional;

	// Se inicializa el shaderLoader
	this->m_shaders.init();

	// Se cargan los shaders
	bool exito = this->m_shaders.loadVertexShader(vertShaderFileName);
	if (!exito) throw std::runtime_error("Falló la carga del vertex shader: " + vertShaderFileName  + "\n" + this->m_shaders.getErrorMsg());

	exito = this->m_shaders.loadFragmentShader(fragShaderFileName);
	if (!exito) throw std::runtime_error("Falló la carga del fragment shader: " + fragShaderFileName + "\n" + this->m_shaders.getErrorMsg());

	// Se linkean y validan los shaders
	exito = this->m_shaders.compilarShaders();
	if (!exito) throw std::runtime_error("Falló el linkeo de los shaders\n" + this->m_shaders.getErrorMsg());
}


void Renderizable::setMaterial(const glm::vec3 &coefDifusion, const glm::vec3 &coefAmbiente, const glm::vec3 &coefEspecular) {
	this->m_coefDifuso = coefDifusion;
	this->m_coefAmbiente = coefAmbiente;
	this->m_coefEspecular = coefEspecular;
}


GLuint Renderizable::cargarShaderProgram() const {
	GLuint shaderProgramID = this->m_shaders.getProgramID();
	glUseProgram(shaderProgramID);

	return shaderProgramID;
}


void Renderizable::cargarMatricesTransformacion(const glm::mat4 &viewMat, const glm::mat4 &projectionMat, const glm::mat4 &modelMat) const {
	// Obtengo el shader program
	GLuint shaderProgramID = this->m_shaders.getProgramID();

	// Primero se obtiene la "location" de cada matriz, todas deben estar definidas en el shader
	const GLint locModelMatrix = glGetUniformLocation(shaderProgramID, "modelMatrix");
	if (locModelMatrix == -1) throw std::runtime_error("No existe el parametro 'modelMatrix' en el shader");

	const GLint locViewMatrix = glGetUniformLocation(shaderProgramID, "viewMatrix");
	if (locViewMatrix == -1) throw std::runtime_error("No existe el parametro 'viewMatrix' en el shader");

	const GLint locProjMatrix = glGetUniformLocation(shaderProgramID, "projectionMatrix");
	if (locProjMatrix == -1) throw std::runtime_error("No existe el parametro 'projectionMatrix' en el shader");

	// Luego se las carga en el shader
	glUniformMatrix4fv(locModelMatrix, 1, GL_FALSE, &modelMat[0][0]);
	glUniformMatrix4fv(locViewMatrix, 1, GL_FALSE, &viewMat[0][0]);
	glUniformMatrix4fv(locProjMatrix, 1, GL_FALSE, &projectionMat[0][0]);
}


void Renderizable::cargarDatosIluminacion(const glm::mat4 &matModel) const {
	// Obtengo el shader program
	GLuint shaderProgramID = this->m_shaders.getProgramID();

	// Primero obtiene los datos de los elementos de iluminacion
	const GLint locDirLuz = glGetUniformLocation(shaderProgramID, "posLuzDireccional"); 

	const GLint locCoefDifusa = glGetUniformLocation(shaderProgramID, "Kd");
	const GLint locCoefAmbiente = glGetUniformLocation(shaderProgramID, "Ka");
	const GLint locCoefEspecular = glGetUniformLocation(shaderProgramID, "Ke");

	// Los carga en el shader; No todos tienen que estar definidos
	if (locDirLuz != -1) glUniform3fv(locDirLuz, 1, &this->m_posLuzDireccional[0]);
	if (locCoefDifusa != -1) glUniform3fv(locCoefDifusa, 1, &this->m_coefDifuso[0]);
	if (locCoefAmbiente != -1) glUniform3fv(locCoefAmbiente, 1, &this->m_coefAmbiente[0]);
	if (locCoefEspecular != -1) glUniform3fv(locCoefEspecular, 1, &this->m_coefEspecular[0]);

	// La matriz de normales para la iluminacion
	const GLint locNormalMat = glGetUniformLocation(shaderProgramID, "normalMatrix");
	if (locNormalMat != 1){
		// No la calcula si no es necesario
		glm::mat3 matNormales = glm::inverseTranspose( glm::mat3(matModel) );
		glUniformMatrix3fv(locNormalMat, 1, GL_FALSE, &matNormales[0][0]);
	}
}


